#!/usr/bin/python
# -*- coding: UTF-8 -*-

import json
import sys
import os
import subprocess
# @brief 检查是否使用了枚举
def check_use_enum(field, dep):
    if field["IdlType"] == "enum":
        dep[field["type"]]=1
    elif field["IdlType"] == "seq":
        if field["key"]["IdlType"] == "enum":
            dep[field["key"]["type"]]=1
    elif field["IdlType"] == "set":
        if field["key"]["IdlType"] == "enum":
            dep[field["key"]["type"]]=1
    elif field["IdlType"] == "dict":
        if field["key"]["IdlType"] == "enum":
            dep[field["key"]["type"]]=1
        elif field["value"]["IdlType"] == "enum":
            dep[field["value"]["type"]]=1

# @brief 检查是否使用了结构体
def check_use_struct(field, dep):
    if field["IdlType"] == "struct":
        dep[field["type"]]=1
    elif field["IdlType"] == "seq":
        if field["key"]["IdlType"] == "struct":
            dep[field["key"]["type"]]=1
    elif field["IdlType"] == "dict":
        if field["value"]["IdlType"] == "struct":
            dep[field["value"]["type"]]=1

# @brief 检查pb 依赖关系返回依赖列表
def check_pb_dep(json_obj):
    if not json_obj:
        return {}

    st_dep = {}
    en_dep = {}
    
    for field in json_obj["fields"]:
        check_use_struct(field, st_dep)
        check_use_enum(field, en_dep)

    return st_dep, en_dep

class FileGenerator:
    def __init__(self, file_name, cpp_json, pb_json = None):
        self.file_name = file_name
        self.file_name_split = file_name.split(".")
        self.namespace = self.file_name_split[0]
        self.cpp_json = cpp_json
        self.pb_json = pb_json

        self.gen_struct_pb_file()
        self.gen_enum_pb_file()
        #生成所有的proto 当前目录下的
        os.system("protoc --csharp_out=. *.proto")

        cmd = subprocess.Popen(["protoc", "--csharp_out=.", "*.proto"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        ret = cmd.communicate()


        file = open(self.file_name_split[0] + ".service.protobuf.serializer.cs", "w")
        file.write("// Machine generated code\n\n")
        file.write("#region Machine generated code\n\n")
        file.write("using kratos;\n")
        file.write("using RingBuffer;\n")
        file.write("using Google.Protobuf;\n")
        file.write('using rpc;\n')
        file.write("using " + self.file_name_split[0].capitalize() + ";\n")
        file.write("using System.Collections.Generic;\n\n")
        self.gen_pb_clear(file)
        self.gen_pb_callparam_serializer(file)
        self.gen_pb_return_serializer(file)
        file.write("#endregion Machine generated code\n\n")
        file.close()

    # @brief enum 单独生成到独立的文件里面
    def gen_enum_pb_file(self):
        if ("enums" in self.cpp_json) and (self.cpp_json["enums"] is not None):
            for enum in self.cpp_json["enums"]:
                file = open(enum["name"]+".enum.proto", "w")
                file.write("// Machine generated code\n\n")
                file.write('syntax = "proto3";\n\n')
                file.write("package rpcdata;\n\n")
                file.write("enum " + enum["name"] + " {\n")
                file.write("    default_" + enum["name"] + " = 0;\n")
                if "fields" in enum:
                    for field in enum["fields"]:
                        file.write("    " + field["name"] + " = " + str(field["value"]) + ";\n")
                file.write("}\n\n")

    # @brief struct 独立生成自己的 proto
    def gen_struct_pb_file(self):
        if ("structs" in self.pb_json) and (self.pb_json["structs"] is not None):
            for struct in self.pb_json["structs"]:
                file = open(struct["name"] + ".struct.proto", "w")
                file.write("// Machine generated code\n\n")
                file.write('syntax = "proto3";\n\n')

                stdep, endep = check_pb_dep(struct)
                for k in stdep.keys():
                    file.write('import "' + k + '.struct.proto";\n')

                for k in endep.keys():
                    file.write('import "' + k + '.enum.proto";\n')

                file.write('\n')
                file.write("package rpcdata;\n\n")
                file.write("message " + struct["name"] + " {\n")
                n = 1
                for field in struct["fields"]:
                    if field["IdlType"] == "dict":
                        #file.write("    " + field["type"] + "<" + field["key"]["type"] + "," +   field["value"]["type"] + "> " + field["name"] + " = " + str(n) + ";\n")
                        Idlstr = "    " + field["type"] + "<"
                        if field["key"]["type"] == "enum":
                            Idlstr += "rpcdata."
                        Idlstr += field["key"]["type"] + ","
                        if field["value"]["type"] == "enum":
                            Idlstr += "rpcdata."
                        Idlstr += field["value"]["type"] + "> " + field["name"] + " = " + str(n) + ";\n"
                        file.write(Idlstr)
                    elif field["IdlType"] == "seq" or field["IdlType"] == "set":
                        #file.write("    " + field["type"] + " " + field["key"]["type"] + " " + field["name"] + " = " + str(n) + ";\n")
                        Idlstr = "    " + field["type"] + " "
                        if field["key"]["type"] == "enum":
                            Idlstr += "rpcdata."
                        Idlstr += field["key"]["type"] + " " + field["name"] + " = " + str(n) + ";\n"
                        file.write(Idlstr)
                    else:
                        file.write("    " + field["type"] + " " + field["name"] + " = " + str(n) + ";\n")
                    n += 1
                file.write("}\n\n")

        file = open(self.file_name_split[0] + ".service.proto", "w")
        file.write("// Machine generated code\n\n")

        file.write('syntax = "proto3";\n\n')

        if ("structs" in self.pb_json) and (self.pb_json["structs"] is not None):
            for onest in self.pb_json["structNames"]:
                file.write('import "' + onest + '.struct.proto";\n')

        if ("enums" in self.pb_json) and (self.pb_json["enums"] is not None):
            for onest in self.pb_json["enumNames"]:
                file.write('import "' + onest + '.enum.proto";\n')

        file.write("package " + self.file_name_split[0] + ";\n\n")


        self.gen_method_pb_file(file)
        self.gen_method_ret_pb(file)
        file.close()

    def gen_pb_callparam_serializer(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        if not ("services" in self.cpp_json):
            return
        for service in self.cpp_json["services"]:
            file.write("namespace "+self.namespace+" {\n\n")
            file.write("internal class " + service["name"] + "CallParamSerializer {\n")
            for method in service["methods"]:
                no_param = False
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        no_param = True
                        break
                if not no_param:
                    file.write("    private static Google.Protobuf.MessageParser<" + service["name"] + "_" + method["name"] +"_args> "  + service["name"] + "_" + method["name"] + "_args_parser = new Google.Protobuf.MessageParser<" + service["name"] + "_" + method["name"] +"_args>(()=>ObjectPoolPlain<" + service["name"] + "_" + method["name"] +"_args>.Get());\n")
            for method in service["methods"]:
                no_param = False
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        no_param = True
                        break
                file.write("    public static void " + method["name"] + "_deserialize(" + service["name"] + "_" + method["name"] + "_CallParam param, GrowingRingBuffer<byte> rb, uint length) {\n")
                if not no_param:
                    file.write("        byte[] buffer = null;\n")
                    file.write("        try {\n")
                    file.write("            if (length == 0) { return; }\n")
                    file.write("            buffer = GlobalBytePool.Pool.Rent((int)length);\n")
                    file.write("            for (int i = 0; i < (int)length; i++) {\n")
                    file.write("                buffer[i] = rb.Get();\n")
                    file.write("            }\n")
                    file.write("            " + service["name"] + "_" + method["name"] + "_args args = " + service["name"] + "_" + method["name"] + "_args_parser.ParseFrom(buffer, 0, (int)length);\n")
                n = 1;
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        break
                    elif arg["IdlType"] == "set":
                        file.write("            foreach (var k in args.Arg" + str(n) + ") {\n")
                        file.write('                (param.arg{index} ??= new HashSet<{type}>()).Add(k);\n'.format(index=n,type=arg["key"]["type"]))
                        #file.write('                if ( param.arg{index} == null){{ param.arg{index} = new HashSet<{type}>(); }}\n'.format(index=n,type=arg["key"]["type"]))
                        #if arg["key"]["IdlType"] == "enum":
                        #    file.write("                param.arg" + str(n) + ".Add((rpc." + arg["key"]["type"] + ")k);\n")
                        #else:
                        #    file.write("                param.arg" + str(n) + ".Add(k);\n")
                        file.write("            }\n")
                    elif arg["IdlType"] == "seq":
                        if arg["key"]["IdlType"] != "struct":
                            file.write("            foreach (var k in args.Arg" + str(n) + ") {\n")
                            if arg["key"]["IdlType"] == "enum":
                                file.write('                (param.arg{index} ??= new List<{type}>()).Add(({type})k);\n'.format(index=n, type=arg["key"]["type"]))
                            else:
                                file.write('                (param.arg{index} ??= new List<{type}>()).Add(k);\n'.format(index=n, type=arg["key"]["type"]))
                            file.write("            }\n")
                        else:
                            file.write("            foreach (var k in args.Arg" + str(n) + ") {\n")
                            file.write('                (param.arg{index} ??= new List<{type}>()).Add(new {type}Impl(k));\n'.format(index=n, type=arg["key"]["type"]))
                            file.write("            }\n")                        
                    elif arg["IdlType"] == "dict":
                        if arg["value"]["IdlType"] == "struct":
                            # 注意这里的比较最好放里面，这样在需要用的时候才去进行序列化
                            file.write("            foreach (var k in args.Arg" + str(n) + ") {\n")
                            file.write('                (param.arg{index} ??= new Dictionary<{dkey},{dvalue}>()).Add(k.Key, new {dvalue}Impl(k.Value));\n'.format(index=n, dkey=arg["key"]["type"], dvalue=arg["value"]["type"]))
                            file.write("            }\n")
                        else:
                            file.write("            foreach (var k in args.Arg" + str(n) + ") {\n")
                            if arg["value"]["IdlType"] == "enum":
                                file.write('                    (param.arg{index} ??= new Dictionary<{dkey},{dvalue}>()).Add(k.Key, ({dvalue})k.Value);\n'.format(index=n, dkey=arg["key"]["type"], dvalue=arg["value"]["type"]))
                            else:
                                file.write('                    (param.arg{index} ??= new Dictionary<{dkey},{dvalue}>()).Add(k.Key, k.Value);\n'.format(index=n, dkey=arg["key"]["type"], dvalue=arg["value"]["type"]))
                            file.write("            }\n")
                    elif arg["IdlType"] == "struct":
                        file.write('            param.arg{index} = new {stype}Impl(args.Arg{index});\n'.format(index=n,stype=arg["type"]))
                    else:
                        if arg["IdlType"] != "string":
                            file.write("            param.arg" + str(n) + " = (" + arg["type"] + ")args.Arg" + str(n) + ";\n")
                        else:
                            file.write("            param.arg" + str(n) + " = args.Arg" + str(n) + ";\n")
                    n += 1
                if not no_param:
                    file.write("            PbParamClear." + service["name"] + "_" + method["name"] +"_ParamClear(args);\n")
                    file.write("            ObjectPoolPlain<" + service["name"] + "_" + method["name"] + "_args>.Recycle(args);\n")
                if not no_param:
                    file.write("        } finally {\n")
                    file.write("            if (buffer != null) {\n")
                    file.write("                kratos.GlobalBytePool.Pool.Return(buffer);\n")
                    file.write("            }\n")
                    file.write("        }\n")
                file.write("    }\n")

                file.write("    public static byte[] " + method["name"] + "_serialize(" + service["name"] + "_" + method["name"] + "_CallParam param) {\n")
                if not no_param:
                    file.write("        if (param.NoParam) { return null; }\n")
                    file.write("        " + service["name"] +"_" + method["name"] + "_args args = kratos.ObjectPoolPlain<" + service["name"] +"_" + method["name"] + "_args>.Get();\n")
                    file.write("        byte[] buffer = null;\n")
                    file.write('        try{\n')
                n = 1;
                for arg in method["arguments"]:
                    if arg["IdlType"] == "void":
                        break
                    elif arg["IdlType"] == "set":
                        file.write("            foreach (var k in param.arg" + str(n) + ") {\n")
                        if arg["key"]["IdlType"] == "enum":
                            file.write("                args.Arg" + str(n) + ".Add((" + "Rpcdata." + arg["key"]["type"] + ")k);\n")
                        else:
                            file.write("                args.Arg" + str(n) + ".Add(k);\n")
                        file.write("            }\n")
                    elif arg["IdlType"] == "seq":
                        if arg["key"]["IdlType"] != "struct":
                            file.write("            foreach (var k in param.arg" + str(n) + ") {\n")
                            if arg["key"]["IdlType"] == "enum":
                                file.write("                args.Arg" + str(n) + ".Add((" + "Rpcdata." + arg["key"]["type"] + ")k);\n")
                            else:
                                file.write("                args.Arg" + str(n) + ".Add(k);\n")
                            file.write("            }\n")
                        else:
                            file.write("            foreach (var k in param.arg" + str(n) + ") {\n")
                            file.write("                args.Arg" + str(n) + ".Add(((" + arg["key"]["type"] + "Impl)k).PBObject);\n")
                            file.write("            }\n")
                    elif arg["IdlType"] == "dict":
                        if arg["value"]["IdlType"] == "struct":
                            file.write("            foreach (var k in param.arg" + str(n) + ") {\n")
                            file.write("                args.Arg" + str(n) + ".Add(k.Key, ((" + arg["value"]["type"] + "Impl)k.Value)?.PBObject);\n")
                            file.write("            }\n")
                        else:
                            file.write("            foreach (var k in param.arg" + str(n) + ") {\n")
                            if arg["value"]["IdlType"] == "enum":
                                file.write("                args.Arg" + str(n) + ".Add(k.Key, (" + "Rpcdata." + arg["value"]["type"] + ")k.Value);\n")
                            else:
                                file.write("                args.Arg" + str(n) + ".Add(k.Key, k.Value);\n")
                            file.write("            }\n")
                    elif arg["IdlType"] == "struct":
                        file.write("            args.Arg" + str(n) + " = ((" + arg["type"] + "Impl)(param.arg" + str(n) + "))?.PBObject;\n")
                    else:
                        if arg["IdlType"] == "enum":
                            file.write("            args.Arg" + str(n) + " = (" + "Rpcdata." + arg["type"] + ")param.arg" + str(n) + ";\n")
                        else:
                            file.write("            args.Arg" + str(n) + " = param.arg" + str(n) + ";\n")
                    n += 1
                if not no_param:
                    file.write("            int size = args.CalculateSize();\n")
                    file.write("            param.Length = (uint)size;\n")
                    file.write("            buffer = GlobalBytePool.Pool.Rent(size);\n")
                    file.write("            using (CodedOutputStream stream = new CodedOutputStream(buffer)) {\n")
                    file.write("                args.WriteTo(stream);\n")
                    file.write("            }\n")
                    file.write('        } finally {\n')
                    file.write('            PbParamClear.{service_name}_{method_name}_ParamClear(args);\n            kratos.ObjectPoolPlain<{service_name}_{method_name}_args>.Recycle(args);\n        }}\n'.format(service_name=service["name"], method_name=method["name"]))
                    file.write("        return buffer;\n")
                else:
                    file.write("        return null;\n")
                file.write("    }\n")
            file.write("}\n\n")    
            file.write("}\n\n")

    def gen_pb_return_serializer(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.cpp_json["services"]:
            file.write("namespace "+self.namespace+" {\n\n")
            file.write("internal class " + service["name"] + "CallReturnSerializer {\n")
            if "methods" in service:
                for method in service["methods"]:
                    if method["retType"]["IdlType"] != "void":
                        file.write("    private static Google.Protobuf.MessageParser<" + service["name"] + "_" + method["name"] +"_ret> "  + service["name"] + "_" + method["name"] + "_ret_parser = new Google.Protobuf.MessageParser<" + service["name"] + "_" + method["name"] +"_ret>(()=>ObjectPoolPlain<" + service["name"] + "_" + method["name"] +"_ret>.Get());\n")
                for method in service["methods"]:
                    ret_string = ""
                    if method["retType"]["IdlType"] != "void":                    
                        if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                            ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ">"
                        elif method["retType"]["IdlType"] == "dict":
                            ret_string = method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," + method["retType"]["value"]["type"] + ">"
                        elif method["retType"]["IdlType"] == "string" or method["retType"]["IdlType"] == "struct":
                            ret_string = method["retType"]["type"]
                        else:
                            ret_string = method["retType"]["type"]
                    file.write("    public static void " + method["name"] + "_deserialize(" + service["name"] + "_" + method["name"] + "_CallReturn param, GrowingRingBuffer<byte> rb, uint length) {\n")
                    if method["retType"]["IdlType"] != "void":                    
                        file.write("        if (length == 0) { return; }\n")
                        file.write("        byte[] buffer = null;\n")
                        file.write("        " + service["name"] + "_" + method["name"] + "_ret ret = null;\n")
                        file.write("        try {\n")
                        file.write("            buffer = GlobalBytePool.Pool.Rent((int)length);\n")
                        file.write("            for (int i = 0; i < (int)length; i++) {\n")
                        file.write("                buffer[i] = rb.Get();\n")
                        file.write("            }\n")
                        file.write("            ret = " + service["name"] + "_" + method["name"] + "_ret_parser.ParseFrom(buffer, 0, (int)length);\n")
                        file.write("        } finally {\n")
                        file.write("            GlobalBytePool.Pool.Return(buffer);\n")
                        file.write("        }\n")
                    if method["retType"]["IdlType"] == "void":
                        pass
                    elif method["retType"]["IdlType"] == "set":
                        file.write("        param.ret = new " + ret_string + "();\n")
                        file.write("        if (ret != null) {\n")
                        file.write("            foreach (var k in ret.Ret) {\n")
                        if method["retType"]["key"]["type"] == "enum":
                            file.write("                param.ret.Add((rpc." + method["retType"]["key"]["type"] + ")k);\n")
                        else:
                            file.write("                param.ret.Add((" + method["retType"]["key"]["type"] + ")k);\n")
                        file.write("            }\n")
                        file.write("        }\n")
                    elif method["retType"]["IdlType"] == "seq":
                        if method["retType"]["key"]["IdlType"] == "struct":
                            file.write("        param.ret = new " + ret_string + "();\n")
                            file.write("        if (ret != null) {\n")
                            file.write("            foreach (var k in ret.Ret) {\n")
                            file.write("                param.ret.Add(new " + method["retType"]["key"]["type"] + "Impl(k));\n")
                            file.write("            }\n")
                            file.write("        }\n")
                        else:
                            file.write("        param.ret = new " + ret_string + "();\n")
                            file.write("        if (ret != null) {\n")
                            file.write("            foreach (var k in ret.Ret) {\n")
                            if method["retType"]["key"]["IdlType"] == "enum":
                                file.write("                param.ret.Add((rpc." + method["retType"]["key"]["type"]  + ")k);\n")
                            else:
                                file.write("                param.ret.Add(k);\n")
                            file.write("            }\n")
                            file.write("        }\n")
                    elif method["retType"]["IdlType"] == "dict":
                        file.write("        param.ret = new " + ret_string + "();\n")
                        if method["retType"]["value"]["IdlType"] == "struct":
                            file.write("        if (ret != null) {\n")
                            file.write("            foreach (var k in ret.Ret) {\n")
                            file.write("                param.ret.Add(k.Key, new " + method["retType"]["value"]["type"]  + "Impl(k.Value));\n")
                            file.write("            }\n")
                            file.write("        }\n")
                        elif method["retType"]["value"]["IdlType"] == "enum":
                            file.write("        if (ret != null) {\n")
                            file.write("            foreach (var k in ret.Ret) {\n")
                            file.write("                param.ret.Add(k.Key, (rpc." + method["retType"]["value"]["type"] + ")k.Value);\n")
                            file.write("            }\n")
                            file.write("        }\n")
                        else:
                            file.write("        if (ret != null) {\n")
                            file.write("            foreach (var k in ret.Ret) {\n")
                            file.write("                param.ret.Add(k.Key, (" + method["retType"]["value"]["type"] + ")k.Value);\n")
                            file.write("            }\n")
                            file.write("        }\n")
                    elif method["retType"]["IdlType"] == "struct":
                        file.write("        param.ret = new " + ret_string + "Impl();\n")
                        file.write("        if (ret != null) {\n")
                        file.write("            ((" + method["retType"]["type"] + "Impl)(param.ret)).PBObject = ret.Ret;\n")
                        file.write("        }\n")
                    else:
                        file.write("        if (ret != null) {\n")                    
                        if method["retType"]["IdlType"] != "string":
                            file.write("            param.ret = (" + method["retType"]["type"] + ")ret.Ret;\n")
                        else:
                            file.write("            param.ret = ret.Ret;\n")
                        file.write("        }\n")
                    if method["retType"]["IdlType"] != "void":
                        file.write("        PbRetClear." + service["name"] +"_" + method["name"] + "_RetClear(ret);\n")
                        file.write("        kratos.ObjectPoolPlain<" + service["name"] +"_" + method["name"] + "_ret>.Recycle(ret);\n")
                        file.write("        ProxyCallAwaiter<" + ret_string + "> waiter = (ProxyCallAwaiter<" + ret_string + ">)Singleton<ManagerThreaded<uint, ProxyCall>>.Instance.Get(param.CallID);\n")
                        file.write("        if (waiter != null) {\n")
                        file.write("            waiter.SetResult(param.ret);\n")  
                        file.write("        }\n")
                    file.write("    }\n")
                    file.write("    public static byte[] " + method["name"] + "_serialize(" + service["name"] + "_" + method["name"] + "_CallReturn param) {\n")
                    if method["retType"]["IdlType"] != "void":
                        file.write("        " + service["name"] +"_" + method["name"] + "_ret ret = ObjectPoolPlain<" + service["name"] +"_" + method["name"] + "_ret>.Get();\n")
                        file.write("        try{\n")
                    if method["retType"]["IdlType"] == "void":
                        pass
                    elif method["retType"]["IdlType"] == "set":
                        file.write("        foreach (var k in param.ret) {\n")
                        if method["retType"]["key"]["IdlType"] == "enum":
                            file.write("            ret.Ret.Add((" + "Rpcdata." + method["retType"]["key"]["type"] + ")k);\n")
                        else:
                            file.write("            ret.Ret.Add(k);\n")
                        file.write("        }\n")
                    elif method["retType"]["IdlType"] == "seq":
                        if method["retType"]["key"]["IdlType"] != "struct":
                            file.write("        foreach (var k in param.ret) {\n")
                            if method["retType"]["key"]["IdlType"] == "enum":
                                file.write("            ret.Ret.Add((" + "Rpcdata." + method["retType"]["key"]["type"] + ")k);\n")
                            else:
                                file.write("            ret.Ret.Add(k);\n")
                            file.write("        }\n")
                        else:
                            file.write("        foreach (var k in param.ret) {\n")
                            # 对于结构体，框架并没有进行拦截，所以这里是可能为空的，如果是空就不要添加进去了
                            file.write("            ret.Ret.Add(((" + method["retType"]["key"]["type"] + "Impl)k)?.PBObject);\n")
                            file.write("        }\n")
                    elif method["retType"]["IdlType"] == "dict":
                        if method["retType"]["value"]["IdlType"] == "struct":
                            file.write("        foreach (var k in param.ret) {\n")
                            file.write("            ret.Ret.Add(k.Key, ((" + method["retType"]["value"]["type"] + "Impl)(k.Value))?.PBObject);\n")
                            file.write("        }\n")
                        else:
                            file.write("        foreach (var k in param.ret) {\n")
                            if method["retType"]["value"]["IdlType"] == "enum":
                                file.write("            ret.Ret.Add(k.Key, (" + "Rpcdata." + method["retType"]["value"]["type"] + ")k.Value);\n")
                            else:
                                file.write("            ret.Ret.Add(k.Key, k.Value);\n")
                            file.write("        }\n")
                    elif method["retType"]["IdlType"] == "struct":
                        file.write("        ret.Ret = ((" + method["retType"]["type"] + "Impl)(param.ret))?.PBObject;\n")
                    else:
                        if method["retType"]["IdlType"] == "enum":
                            file.write("        ret.Ret = (" + "Rpcdata." + method["retType"]["type"] +")param.ret;\n")
                        else:
                            file.write("        ret.Ret = param.ret;\n")
                    if method["retType"]["IdlType"] == "void":
                        file.write("        return null;\n")
                        file.write("    }\n")
                    else:
                        file.write("        } catch(System.Exception){\n            param.ErrorID = (uint)RpcError.rpc_exception;\n        }\n") 
                        file.write("        int size = ret.CalculateSize();\n")
                        file.write("        param.Length = (uint)size; \n")
                        file.write("        byte[] buffer = GlobalBytePool.Pool.Rent(size);\n")
                        file.write("        using (CodedOutputStream stream = new CodedOutputStream(buffer)) {\n")
                        file.write("            ret.WriteTo(stream);\n")
                        file.write("        }\n")
                        file.write("        PbRetClear." + service["name"] +"_" + method["name"] + "_RetClear(ret);\n")
                        file.write("        kratos.ObjectPoolPlain<" + service["name"] +"_" + method["name"] + "_ret>.Recycle(ret);\n")
                        file.write("        return buffer;\n")
                        file.write("    }\n")                
            file.write("}\n\n")
            file.write("}\n\n")

    def gen_pb_clear(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        file.write("namespace "+self.namespace+" {\n\n")
        file.write("internal static class PbParamClear {\n")
        if "services" in self.cpp_json:
            for service in self.cpp_json["services"]:
                for method in service["methods"]:
                    no_param = False
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "void":
                            no_param = True
                            break
                    if no_param:
                        continue                
                    file.write("        internal static void " + service["name"] + "_" + method["name"] + "_ParamClear(" + service["name"] + "_" + method["name"] + "_args args) {\n")
                    file.write("            if (args == null) { return; }\n")
                    n = 1
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "seq" or arg["IdlType"] == "set" or arg["IdlType"] == "dict":                    
                            file.write("            args.Arg" + str(n) + ".Clear();\n")
                        elif arg["IdlType"] == "struct":
                            file.write("            args.Arg" + str(n) + " = null;\n")
                        elif arg["IdlType"] == "bool":
                            file.write("            args.Arg" + str(n) + " = false;\n")
                        elif arg["IdlType"] == "string":
                            file.write("            args.Arg" + str(n) + " = \"\";\n")
                        else:
                            file.write("            args.Arg" + str(n) + " = 0;\n")
                        n += 1
                    file.write("        }\n")
        file.write("}\n\n")
        file.write("internal static class PbRetClear {\n")
        if "services" in self.cpp_json:
            for service in self.cpp_json["services"]:
                for method in service["methods"]:
                    if method["retType"]["IdlType"] == "void":
                        continue
                    file.write("        internal static void " + service["name"] + "_" + method["name"] + "_RetClear(" + service["name"] + "_" + method["name"] + "_ret ret) {\n")               
                    file.write("            if (ret == null) { return; }\n")
                    if method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set" or method["retType"]["IdlType"] == "dict":                    
                        file.write("            ret.Ret.Clear();\n")
                    elif method["retType"]["IdlType"] == "struct":
                        file.write("            ret.Ret = null;\n")
                    elif method["retType"]["IdlType"] == "bool":
                        file.write("            ret.Ret = false;\n")
                    elif method["retType"]["IdlType"] == "string":
                        file.write("            ret.Ret = \"\";\n")
                    else:
                        file.write("            ret.Ret = 0;\n")
                    file.write("        }\n")
        file.write("}\n\n")
        file.write("}\n\n")
        
    def gen_method_ret_pb(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.pb_json["services"]:
            for method in service["methods"]:
                file.write("message " + service["name"] + "_" + method["name"] + "_ret {\n")
                if method["retType"]["IdlType"] == "struct" or method["retType"]["IdlType"] == "enum":
                    file.write("    rpcdata." + method["retType"]["type"] + " ret = 1;\n")
                elif method["retType"]["IdlType"] == "dict":
                    if method["retType"]["value"]["IdlType"] == "struct" or method["retType"]["value"]["IdlType"] == "enum":
                        file.write("    " + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + ", rpcdata." +   method["retType"]["value"]["type"] + "> " + " ret = 1;\n")
                    else:
                        file.write("    " + method["retType"]["type"] + "<" + method["retType"]["key"]["type"] + "," +   method["retType"]["value"]["type"] + "> " + " ret = 1;\n")
                elif method["retType"]["IdlType"] == "seq" or method["retType"]["IdlType"] == "set":
                    if method["retType"]["key"]["IdlType"] == "struct" or method["retType"]["key"]["IdlType"] == "enum":
                        file.write("    " + method["retType"]["type"] + " rpcdata." + method["retType"]["key"]["type"] + " ret = 1;\n")
                    else:
                        file.write("    " + method["retType"]["type"] + " " + method["retType"]["key"]["type"] + " ret = 1;\n")
                elif method["retType"]["IdlType"] == "void":
                    pass
                else:
                    file.write("    " + method["retType"]["type"] + " ret = 1;\n")
                file.write("}\n\n")
        
    def gen_struct_pb_serializer(self, file):
        if (not ("structs" in self.pb_json)) or (self.pb_json["structs"] is None):
            return
        for struct in self.pb_json["structs"]:
            file.write("void serialize(const " + struct["name"] + "& msg, " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg);\n")
        file.write("\n")
        for struct in self.pb_json["structs"]:
            file.write("void serialize(const " + struct["name"] + "& msg, " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg) {\n")
            for field in struct["fields"]:
                if field["IdlType"] == "dict":
                    file.write("    for (auto& [k, v] : msg." + field["name"] + ") {\n")
                    if field["value"]["IdlType"] == "struct":
                        file.write("        serialize(v, (*pb_msg.mutable_" + field["name"] + "())[k]);\n")
                    else:
                        file.write("        (*pb_msg.mutable_" + field["name"] + "())[k] = v;\n")
                    file.write("    }\n")
                elif field["IdlType"] == "seq" or field["IdlType"] == "set":
                    file.write("    for (auto& i : msg." + field["name"] + ") {\n")
                    if field["key"]["IdlType"] == "struct":
                        file.write("        serialize(i, *pb_msg.add_" + field["name"] + "());\n")
                    else:
                        file.write("        pb_msg.add_" + field["name"] + "(i);\n")
                    file.write("    }\n")
                elif field["IdlType"] == "struct":
                    file.write("    serialize(msg." + field["name"] + ", *pb_msg.mutable_" + field["name"] + "());\n")
                else:
                    file.write("    pb_msg.set_" + field["name"] + "(msg." +  field["name"] + ");\n")
            file.write("}\n\n")
            
    def gen_pb_struct_serializer(self, file):
        if (not ("structs" in self.pb_json)) or (self.pb_json["structs"] is None):
            return
        for struct in self.pb_json["structs"]:
            file.write("void serialize(const " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg, " +  struct["name"] + "& msg);\n")
        file.write("\n")
        for struct in self.pb_json["structs"]:
            file.write("void serialize(const " + self.file_name_split[0] + "::" + struct["name"] + "& pb_msg, " +  struct["name"] + "& msg) {\n")
            for field in struct["fields"]:
                if field["IdlType"] == "dict":
                    fieldName = "pb_msg." + field["name"]
                    file.write("    for (auto it = " + fieldName + "().begin(); it != " + fieldName + "().end(); it++) {\n")
                    if field["value"]["IdlType"] == "struct":
                        file.write("        serialize(it->second, msg." + field["name"] + "[it->first]);\n")
                    else:
                        file.write("        msg." + field["name"] + "[it->first] = it->second;\n")
                    file.write("    }\n")
                elif field["IdlType"] == "seq":
                    file.write("    msg." + field["name"] + ".resize(pb_msg." + field["name"] + "_size());\n")
                    file.write("    for (int i = 0; i < pb_msg." + field["name"] + "_size(); i++) {\n")
                    if field["key"]["IdlType"] == "struct":
                        file.write("        serialize(pb_msg." + field["name"] + "(i), msg." + field["name"]+ "[i]);\n")
                    else:
                        file.write("        msg." + field["name"] + "[i] = i;\n")
                    file.write("    }\n")
                elif field["IdlType"] == "set":
                    file.write("    for (int i = 0; i < pb_msg." + field["name"] + "_size(); i++) {\n")
                    if field["key"]["IdlType"] == "struct":
                        file.write("        serialize(pb_msg." + field["name"] + "(i), msg." + field["name"]+ "[i]);\n")
                    else:
                        file.write("        msg." + field["name"] + ".insert(pb_msg." + field["name"] + "(i));\n")
                    file.write("    }\n")
                elif field["IdlType"] == "struct":
                    file.write("    serialize(pb_msg." + field["name"] + "(), msg." + field["name"] + ");\n")
                else:
                    file.write("    msg." + field["name"] + " = pb_msg." +  field["name"] + "();\n")
            file.write("}\n\n")
            
    def gen_method_pb_file(self, file):
        if (not ("services" in self.cpp_json)) or (self.cpp_json["services"] is None):
            return
        for service in self.pb_json["services"]:
            if "methods" in service:
                for method in service["methods"]:
                    file.write("message " + service["name"] + "_" + method["name"] + "_args {\n")
                    n = 1
                    for arg in method["arguments"]:
                        if arg["IdlType"] == "struct" or arg["IdlType"] == "enum":
                            file.write("    rpcdata." + arg["type"])
                        elif arg["IdlType"] == "dict":
                            if arg["value"]["IdlType"] == "struct" or arg["value"]["IdlType"] == "enum":
                                file.write("    " + arg["type"] + "<" + arg["key"]["type"] + ", rpcdata." +   arg["value"]["type"] + ">")
                            else:
                                file.write("    " + arg["type"] + "<" + arg["key"]["type"] + "," +   arg["value"]["type"] + ">")

                        elif arg["IdlType"] == "seq" or arg["IdlType"] == "set":
                            if arg["key"]["IdlType"] == "struct" or arg["key"]["IdlType"] == "enum":
                                file.write("    " + arg["type"] + " rpcdata." + arg["key"]["type"])
                            else:
                                file.write("    " + arg["type"] + " " + arg["key"]["type"])
                        elif arg["IdlType"] == "void":
                            continue
                        else:
                            file.write("    " + arg["type"])
                        file.write(" arg" + str(n) + " = " + str(n) + ";\n") 
                        n += 1
                    file.write("}\n\n")

if __name__ == "__main__":
    cppjson = json.load(open(sys.argv[1]))
    pbjson = json.load(open(sys.argv[2]))
    FileGenerator(sys.argv[1], cppjson, pbjson)
